#include "EditorReflectedPropertyModel.h"
#include "Object/NewObject.h"

TSharedObjectPtr<REditorReflectedPropertyModel> REditorReflectedPropertyModel::Create(TSharedObjectPtr<RObject> InValueObject, RObject *InParent)
{
    TSharedObjectPtr<REditorReflectedPropertyModel> NewModel = NewObject<REditorReflectedPropertyModel>(InParent);
    NewModel->Object = InValueObject;

    return NewModel;
}

void REditorPropertyOperation::ChangeValue(
    RObjectPtr InObject
    , const TVector<CString> &InPropertyPath
    , CAny InOldValue
    , CAny InNewValue)
{
    PropertyPath = InPropertyPath;
    OldValue = InOldValue;
    NewValue = InNewValue;

    Objects.Add(InObject);

    RedoFunction.Bind([this](TSharedObjectPtr<REditorPageModel> PageModel)
    {
        void* Value= GetPropertyValue();
        CType* Type= OldValue.GetType();

        //erase old value
        Type->Instancable->EraseInstance(Value);
        Type->Instancable->CloneInstance(NewValue.GetPointer(),Value);
    
        OnUndoRedo.ExecuteIfBound();
    });

    UndoFunction.Bind([this](TSharedObjectPtr<REditorPageModel> PageModel)
    {
        void* Value= GetPropertyValue();
        CType* Type= OldValue.GetType();

        //erase old value
        Type->Instancable->EraseInstance(Value);
        Type->Instancable->CloneInstance(OldValue.GetPointer(),Value);

        OnUndoRedo.ExecuteIfBound();
    });
}

void REditorPropertyOperation::ChangeVirtualPropertyValue(RObjectPtr InObject, CProperty *InProperty, CAny InOldValue, CAny InNewValue)
{
    Property = InProperty;
    OldValue = InOldValue;
    NewValue = InNewValue;

    Objects.Add(InObject);

    RedoFunction.Bind([this,InProperty](TSharedObjectPtr<REditorPageModel> PageModel)
    {
        InProperty->NonStaticProperty->SetValue(Objects[0].Get(),NewValue);
        OnUndoRedo.ExecuteIfBound();
    });

    UndoFunction.Bind([this,InProperty](TSharedObjectPtr<REditorPageModel> PageModel)
    {
        InProperty->NonStaticProperty->SetValue(Objects[0].Get(),OldValue);
        OnUndoRedo.ExecuteIfBound();
    }); 
}

void *REditorPropertyOperation::GetPropertyValue()
{
    RObjectPtr Object = Objects[0];

    auto Property = Object->GetClass()->FindProperty(PropertyPath[0]);
    void* PropertyValue=Property->FieldProperty->GetValuePointer(Object.Get());

    if(PropertyPath.Num()==1)
    {
        return PropertyValue;
    }

    return GetPropertyValue(Property->GetType().BasicType, PropertyValue, PropertyPath.Slice(1, PropertyPath.Num()));
}

void *REditorPropertyOperation::GetPropertyValue(CType *InParentPropertyType, void *InParentPropertyValue, const TVector<CString> &InPropertyPath)
{
    if(InParentPropertyType->IsVectorType())
    {
        CTVectorGeneralized * VectorType
            = reinterpret_cast<CTVectorGeneralized*>(InParentPropertyType);
        CType* ElementType = VectorType->GetElementType().BasicType;
        CReflectableVector* VectorBase=reinterpret_cast<CReflectableVector*>(InParentPropertyValue);
        
        void* ElementValue= VectorBase->At(ElementType,InPropertyPath[0].ToInt64());

        if(InPropertyPath.Num()==1)
        {
            return ElementValue;
        }

        return GetPropertyValue(ElementType, ElementValue, InPropertyPath.Slice(1, InPropertyPath.Num()));
        
    }
    else
    {
        assert(false&& "do not have sub property");
        return nullptr;
    }
}
